package com.xuexiang.xui_lib.component.imageview.edit;

import ohos.agp.components.VelocityDetector;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 手势识别器
 *
 * @author 裴云飞
 * @date 2020/12/27
 */
public class GestureDetector {
    public interface OnGestureListener {

        /**
         * 发生滚动时回调
         *
         * @param e1 手指按下时的事件
         * @param e2 手指移动时的事件
         * @param distanceX 横向的移动距离
         * @param distanceY 纵向的移动距离
         * @return boolean
         */
        boolean onScroll(TouchEvent e1, TouchEvent e2, float distanceX, float distanceY);


        /**
         * Notified when a tap occurs with the up {@link TouchEvent}
         * that triggered it.
         *
         * @param e The up motion event that completed the first tap
         * @return true if the event is consumed, else false
         */
        boolean onSingleTapUp(TouchEvent e);

        /**
         * Notified when a long press occurs with the initial on down {@link TouchEvent}
         * that trigged it.
         *
         * @param e The initial on down motion event that started the longpress.
         */
        void onLongPress(TouchEvent e);
    }

    private int mTouchSlopSquare;
    private final OnGestureListener mListener;
    private static final int LONG_PRESS = 2;
    private boolean mAlwaysInTapRegion;
    private boolean mIgnoreNextUpEvent;
    private TouchEvent mCurrentDownEvent;
    //private TouchEvent mPreviousUpEvent;
    private final EventHandler mHandler;
    private float mLastFocusX;
    private float mLastFocusY;
    private float mDownFocusX;
    private float mDownFocusY;
    private boolean mInLongPress;
    private boolean mLongClick;
    private VelocityDetector mVelocityTracker;
    private static final int TOUCH_SLOP = 8;
    private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f;
    private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;
    public static final int CLASSIFICATION_DEEP_PRESS = 2;

    public GestureDetector(OnGestureListener listener) {
        mListener = listener;
        int touchSlop = TOUCH_SLOP;
        // 滑动的时候，手指的移动要大于这个距离才算发生了滚动
        mTouchSlopSquare = touchSlop * touchSlop;
        EventRunner runner = EventRunner.create(false);
        mHandler = new GestureHandler(runner);
    }

    public boolean onTouchEvent(TouchEvent ev) {
        final int action = ev.getAction();
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityDetector.obtainInstance();
        }
        mVelocityTracker.addEvent(ev);

        final boolean pointerUp =
            action == TouchEvent.OTHER_POINT_UP;
        final int skipIndex = pointerUp ? ev.getIndex() : -1;

        float sumX = 0, sumY = 0;
        final int count = ev.getPointerCount();
        // 把所有还在触摸的手指的位置x，y加起来，后面求平均数，算出中心焦点
        for (int i = 0; i < count; i++) {
            if (skipIndex == i) {
                // 跳过非主要指针的抬起动作
                continue;
            }
            sumX += ev.getPointerPosition(i).getX();
            sumY += ev.getPointerPosition(i).getY();
        }
        final int div = pointerUp ? count - 1 : count;
        // 求平均值，算出中心焦点
        final float focusX = sumX / div;
        final float focusY = sumY / div;
        boolean handled = false;

        switch (action) {
            case TouchEvent.OTHER_POINT_DOWN:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                break;
            case TouchEvent.OTHER_POINT_UP:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                break;
            case TouchEvent.PRIMARY_POINT_DOWN:
                mDownFocusX = mLastFocusX = focusX;
                mDownFocusY = mLastFocusY = focusY;
                mCurrentDownEvent = ev;
                mAlwaysInTapRegion = true;
                mInLongPress = false;
                break;

            case TouchEvent.POINT_MOVE:
                if (mInLongPress) {
                    break;
                }

                final float scrollX = mLastFocusX - focusX;
                final float scrollY = mLastFocusY - focusY;

                if (mAlwaysInTapRegion) {
                    final int deltaX = (int) (focusX - mDownFocusX);
                    final int deltaY = (int) (focusY - mDownFocusY);
                    int distance = (deltaX * deltaX) + (deltaY * deltaY);
                    int slopSquare = mTouchSlopSquare;
                    if (distance > slopSquare) {
                        handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                        mLastFocusX = focusX;
                        mLastFocusY = focusY;
                        mAlwaysInTapRegion = false;
                        mHandler.removeEvent(LONG_PRESS);
                    }
                } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                    mLastFocusX = focusX;
                    mLastFocusY = focusY;
                }
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                if (mAlwaysInTapRegion && !mIgnoreNextUpEvent && !mLongClick) {
                    handled = mListener.onSingleTapUp(ev);
                }
                if (mInLongPress) {
                    mInLongPress = false;
                }
                mHandler.removeEvent(LONG_PRESS);
                mIgnoreNextUpEvent = false;
                mLongClick = false;
                break;

            case TouchEvent.CANCEL:
                cancel();
                break;
        }
        return handled;
    }

    /**
     * 设置是否长按
     *
     * @param mLongClick 长按
     */
    public void setMLongClick(boolean mLongClick) {
        this.mLongClick = mLongClick;
    }

    static HiLogLabel lab = new HiLogLabel(HiLog.LOG_APP, 0011, "my");

    public static void showDebugLog(String str) {
        HiLog.debug(lab, "%{public}s", str);
    }

    private void cancel() {
//        mHandler.removeMessages(SHOW_PRESS);
        mHandler.removeEvent(LONG_PRESS);
//        mHandler.removeMessages(TAP);

        mVelocityTracker = null;
//        mIsDoubleTapping = false;
//        mStillDown = false;
        mAlwaysInTapRegion = false;
//        mAlwaysInBiggerTapRegion = false;
//        mDeferConfirmSingleTap = false;
        mInLongPress = false;
//        mInContextClick = false;
        mIgnoreNextUpEvent = false;
    }

    public static class SimpleOnGestureListener implements OnGestureListener {

        @Override
        public boolean onScroll(TouchEvent e1, TouchEvent e2, float distanceX, float distanceY) {
            return false;
        }

        @Override
        public boolean onSingleTapUp(TouchEvent e) {
            return false;
        }

        @Override
        public void onLongPress(TouchEvent e) {

        }
    }

    private class GestureHandler extends EventHandler {

        public GestureHandler(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        public GestureHandler(EventHandler eventHandler) {
            super(eventHandler.getEventRunner());
        }

        @Override
        protected void processEvent(InnerEvent event) {
            switch (event.eventId) {
                case LONG_PRESS:
                    dispatchLongPress();
                    break;
                default:
                    throw new RuntimeException("Unknown message " + event.toString()); //never
            }
        }
    }

    private void dispatchLongPress() {
        mInLongPress = true;
        mListener.onLongPress(mCurrentDownEvent);
    }
}
